package cn.yx.common.core.util.io;

import cn.hutool.core.util.StrUtil;
import cn.yx.common.core.constant.FileConstant;
import cn.yx.common.core.constant.message.io.IoUtilMessageKey;
import cn.yx.common.core.enums.CharsetEnum;
import cn.yx.common.core.exception.CommonException;
import cn.yx.common.core.util.json.jackson.JsonUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.List;

/**
 * <p>IO工具类</p>
 *
 * @author Wgssmart
 */
@Slf4j
public final class IoUtil {

    private IoUtil() {
    }

    /**
     * 获取指定文件内的所有字符文本内容
     *
     * @param filePath    指定文件的路径
     * @param charsetName 编码名称
     * @return 文件的所有文本内容
     */
    public static String readText(String filePath, String charsetName) {
        try {
            return readText(new FileInputStream(filePath), charsetName);
        } catch (FileNotFoundException e) {
            log.error("file {} not found error: {}", filePath, e.getMessage());
            throw new CommonException(IoUtilMessageKey.FILE_NOT_FOUND_ERROR, e);
        }
    }

    /**
     * 以UTF-8编码读取文件内的字符文本内容
     *
     * @param filePath 字符文件路径
     * @return 文件的所有文本内容
     */
    public static String readText(String filePath) {
        return readText(filePath, CharsetEnum.UTF8.getValue());
    }

    /**
     * 获取指定输入流的所有字符文本内容，不会关闭输入流参数
     *
     * @param inputStream 字符文件输入流
     * @param charsetName 编码名称
     * @return 文件的所有文本内容
     */
    public static String readText(InputStream inputStream, String charsetName) {
        StringBuilder text = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charsetName))) {
            String str;
            while ((str = reader.readLine()) != null) {
                if (StrUtil.isEmpty(text.toString())) {
                    text.append(str);
                } else {
                    text.append("\r\n").append(str);
                }
            }
            return text.toString();
        } catch (IOException e) {
            log.error("read text file from input stream error: {}", e.getMessage());
            throw new CommonException(IoUtilMessageKey.GET_TEXT_FROM_INPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 使用UTF-8编码读取指定输入流中的字符文本内容，不会关闭输入流参数
     *
     * @param inputStream 字符文件输入流
     * @return 文件的所有文本
     */
    public static String readText(InputStream inputStream) {
        return readText(inputStream, CharsetEnum.UTF8.getValue());
    }

    /**
     * 写出文本内容到指定路径的文件
     *
     * @param text        字符串内容
     * @param filePath    目标文件路径
     * @param charsetName 编码名称
     */
    public static void writeText(String text, String filePath, String charsetName) {
        try (BufferedWriter bufferedWriter = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream(filePath), charsetName))) {
            bufferedWriter.write(text);
        } catch (IOException e) {
            log.error("write text to file error: {}", e.getMessage());
            throw new CommonException(IoUtilMessageKey.WRITE_TEXT_FILE_ERROR, e);
        }
    }

    /**
     * 使用UTF-8默认编码写出文本内容到指定路径的文件
     *
     * @param text     字符串内容
     * @param filePath 目标文件路径
     */
    public static void writeText(String text, String filePath) {
        writeText(text, filePath, CharsetEnum.UTF8.getValue());
    }

    /**
     * 字节数组写入到指定文件
     *
     * @param bytes    字节数组
     * @param filePath 目标文件路径
     */
    public static void writeData(byte[] bytes, String filePath) {
        try (FileOutputStream fileOutputStream = new FileOutputStream(filePath)) {
            fileOutputStream.write(bytes);
        } catch (IOException e) {
            log.error("write data to file error: {}", e.getMessage());
            throw new CommonException(IoUtilMessageKey.WRITE_DATA_ERROR, e);
        }
    }

    /**
     * 字节数组转化为ByteArrayInputStream
     *
     * @param bytes 字节数组
     * @return ByteArrayInputStream对象
     */
    public static ByteArrayInputStream toInput(byte[] bytes) {
        return new ByteArrayInputStream(bytes);
    }

    /**
     * 获取指定路径文件的字节数组
     *
     * @param filePath 文件路径
     * @return 文件对应的字节数组数据
     */
    public static byte[] readData(String filePath) {
        try {
            return readData(new FileInputStream(filePath));
        } catch (FileNotFoundException e) {
            log.error("file {} not found error: {}", filePath, e.getMessage());
            throw new CommonException(IoUtilMessageKey.FILE_NOT_FOUND_ERROR, e);
        }
    }

    /**
     * 获取输入流对应的字节数组，不会关闭输入流参数
     *
     * @param inputStream 输入流
     * @return 输入流对应的字节数组数据
     */
    public static byte[] readData(InputStream inputStream) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            toOutput(inputStream, outputStream);
            return outputStream.toByteArray();
        } catch (IOException e) {
            log.error("byte array output stream error: {}", e.getMessage());
            throw new CommonException(IoUtilMessageKey.READ_DATA_ERROR, e);
        }
    }

    /**
     * 把指定路径文件的数据放入到指定输出流，不会关闭输出流参数
     *
     * @param filePath     文件路径
     * @param outputStream 输出流对象
     */
    public static void toOutput(String filePath, OutputStream outputStream) {
        try (InputStream inputStream = new FileInputStream(filePath)) {
            toOutput(inputStream, outputStream);
        } catch (IOException e) {
            log.error("read file {} to output stream error: {}", filePath, e.getMessage());
            throw new CommonException(IoUtilMessageKey.TO_OUTPUT_STREAM_ERROR, e);
        }
    }

    /**
     * 把输入流的数据放入到指定输出流，不会关闭输入和输出流参数
     *
     * @param inputStream  输入流
     * @param outputStream 输出流
     */
    public static void toOutput(InputStream inputStream, OutputStream outputStream) {
        byte[] bytes = new byte[FileConstant.DEFAULT_BUFFER_SIZE];
        try {
            int len;
            while ((len = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, len);
            }
        } catch (IOException e) {
            log.error("read input stream to output stream error: {}", e.getMessage());
            throw new CommonException(IoUtilMessageKey.TO_OUTPUT_STREAM_ERROR, e);
        }
    }


    /**
     * 字节输出流转化为字节输入流，不会关闭输出流参数
     *
     * @param byteArrayOutputStream 字符输出流
     * @return ByteArrayInputStream对象
     */
    public static ByteArrayInputStream toInput(ByteArrayOutputStream byteArrayOutputStream) {
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    }

    /**
     * 把JSON文件读入到指定的对象中
     *
     * @param filePath json文件路径
     * @param tClass   指定的目标对象类
     * @param <T>      目标对象泛型
     * @return 由JSON文件转化而来的目标Java对象
     */
    public static <T> T objectFromJsonFile(String filePath, Class<T> tClass) {
        return objectFromJsonStr(readText(filePath), tClass);
    }

    /**
     * 把JSON文件读入到指定的对象中，返回List
     *
     * @param filePath json文件路径
     * @param tClass   指定的目标对象类
     * @param <T>      目标对象泛型
     * @return 由JSON文件转化而来的目标Java对象
     */
    public static <T> List<T> listFromJsonFile(String filePath, Class<T> tClass) {
        return listFromJsonStr(readText(filePath), tClass);
    }

    /**
     * 把JSON字符串读入到指定的对象中
     *
     * @param jsonStr json字符中
     * @param tClass  指定的目标对象类
     * @param <T>     目标对象泛型
     * @return 由JSON文件转化而来的目标Java对象
     */
    public static <T> T objectFromJsonStr(String jsonStr, Class<T> tClass) {
        return JsonUtil.toBean(jsonStr, tClass);
    }

    /**
     * 把JSON字符串读入到指定的对象中，返回List
     *
     * @param jsonStr json字符中
     * @param tClass  指定的目标对象类
     * @param <T>     目标对象泛型
     * @return 由JSON文件转化而来的目标Java对象列表
     */
    public static <T> List<T> listFromJsonStr(String jsonStr, Class<T> tClass) {
        return JsonUtil.toBeanList(jsonStr, tClass);
    }

    /**
     * 把JSON 输入流读入到指定的对象中，不会关闭输入流
     *
     * @param inputStream json InputStream
     * @param tClass      指定的目标对象类
     * @param <T>         目标对象泛型
     * @return 由JSON文件转化而来的目标Java对象
     */
    public static <T> T objectFromJsonInput(InputStream inputStream, Class<T> tClass) {
        return objectFromJsonStr(readText(inputStream, CharsetEnum.UTF8.getValue()), tClass);
    }

    /**
     * 把JSON 输入流读入到指定的对象中，返回List，不会关闭输入流
     *
     * @param inputStream json InputStream
     * @param tClass      指定的目标对象类
     * @param <T>         目标对象泛型
     * @return 由JSON文件转化而来的目标Java对象列表
     */
    public static <T> List<T> listFromJsonInput(InputStream inputStream, Class<T> tClass) {
        return listFromJsonStr(readText(inputStream, CharsetEnum.UTF8.getValue()), tClass);
    }

}
